

let BaseGame = require('BaseGame');

cc.Class({
    extends: BaseGame,
    ctor(){
        this.rowNum = 12;
        this.colNum = 9;
        this.arenaW = 595;
        this.arenaH = 793;
        this.blockW = this.arenaW/this.colNum;
        this.blockH = this.arenaH/this.rowNum;
        this.colorArr = ['紫','绿','蓝','黄'];
        this.blockSpriteMap = {};
        this.previewBlockSpriteMap = {};
        this.arena = [
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,3,3,3,0,0,0],
            [4,4,4,4,0,4,4,4,4],
            [0,0,0,4,4,4,4,0,0],
            [1,1,1,0,0,0,1,1,1],
            [3,3,3,0,3,3,3,2,2],
            [0,0,1,1,3,3,3,0,0],
            [2,2,2,2,0,2,2,1,1],
            [1,1,2,2,0,0,3,3,3],
        ];
        this.arenaBase = [
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0],
            [2,2,0,0,3,3,3,1,1],
            [1,1,0,2,2,0,3,3,3],
        ];        
        this.previewRowData = [0,0,0,0,0,0,0,0,0];
        this.boomRowNodeArr = [];
        this._score = 0;
        this.aniTime = 0.5;
        this.drawReady = true;
    },
    properties: {
        gameBox: cc.Node,
        blockPrefab: cc.Prefab,
        previewBlockPrefab: cc.Prefab,
        arenaNode: cc.Node,
        blockBoxNode: cc.Node,
        previewNode: cc.Node,
        boomRowPrefab: cc.Prefab,
        boomBlockPrefab: cc.Prefab,
        score: {
            get: function(){
                return this._score
            },
            set: function(val){
                this._score = val;
                this.commTopNode.emit('updateNum', {
                    score: ''+val
                })  
            }
        },
    },
    submitCallBack(){
        console.log('提交分数回调')
    },
    startGame(){
        console.log('开始游戏')
    },
    resetGame(){
        console.log('重置游戏！')
        this.drawReady = true;
        this.score = 0;
        this.arena = JSON.parse(JSON.stringify(this.arenaBase));
        this.blockBoxNode.removeAllChildren();
        this.initArena();
        this.createNextRowData();
    },
    gameOver(){
        console.log('游戏结束！')
    },
    initBlockEvent(node){
        node.on('touchstart', (e)=>{
            if(!this.drawReady){
                return false;
            }
            let boundary = this.getBoundary(node);
            node.min = this.fixed(boundary[0]*this.blockW, 2);
            node.max = this.fixed(boundary[1]*this.blockW, 2);
            node.oldX = this.fixed(node.x);

            node.on('touchmove', this.blockTouchmove, this);
            node.on('touchend', this.blockTouchend, this);
            node.on('touchcancel', this.blockTouchend, this);

        }, this);
    },
    fixed(num, fix){
        return Number(num.toFixed(fix));
    },
    blockTouchmove(e){
        let node = e.target;
        node.x+=e.getDeltaX();
        if(node.x <= node.min){
            node.x = node.min;
        }
        if(node.x>=node.max){
            node.x = node.max
        }
    },
    blockTouchend(e){
        this.drawReady = false;
        let node = e.target;
        node.off('touchmove', this.blockTouchmove, this);
        node.off('touchend', this.blockTouchend, this);
        node.off('touchcancel', this.blockTouchend, this);          
        let n = Math.round((node.x - node.oldX)/this.blockW)
        let x = 0;
        if(n != 0){
            x = node.oldX+this.blockW*n
            cc.tween(node).to(0.1, {position: cc.v2(x, node.y)}).call(()=>{
                new Promise((resolve, reject)=>{
                    if(n!=0){
                        this.setRowData(node, n);
                        node.pos[1] = node.pos[1]+n;
                        this.moveRowDown(node.pos[0], ()=>{
                            this.testDestroyRow(()=>{
                                resolve();
                            });
                        });
                    }else{
                        resolve();
                    }
                }).then((err)=>{
                    console.log(err);
                    this.createRowNode(this.createRowData(), this.rowNum);
                    this.moveArena(()=>{
                        this.moveRowDown(this.rowNum-2, ()=>{
                            console.log('检测消除')
                            this.testDestroyRow(()=>{
                                this.drawReady = true;
                            });
                        });  
                    });
                })
            }).start();             
        }else{
            x = node.oldX;
            cc.tween(node).to(0.1, {position: cc.v2(x, node.y)}).start()
            this.drawReady = true;
        }
    },
    testDestroyRow(cb){
        let aNode = [];
        let rowArr = [];
        this.arena.forEach((item, index)=>{
            if(item.every(val=>val != 0)){
                aNode = aNode.concat(this.getRowNode(index));
                this.arena[index] = new Array(this.colNum).fill(0);
                this.score++;
                this.createScoreNode(this.getRowNode(index)[0], 1)
                rowArr.push(index);
            }
        })
        if(aNode.length == 0){
            cb&&cb()
            return false;
        }else{
            let rowNodeArr = [];
            for(let i=0; i<rowArr.length; i++){
                let rowNode = this.createBoomRow(rowArr[i])
                rowNodeArr.push(rowNode);
            }
            setTimeout(()=>{
                rowNodeArr.forEach(item=>{
                    item.isShow = false;
                    item.active = false;
                })
                aNode.forEach(item=>{
                    item.destroy();
                })
                setTimeout(()=>{
                    this.moveRowDown(this.rowNum-2, (isDestroy)=>{
                        console.log('isDestroy', isDestroy)
                        //isDestroy && this.testDestroyRow();
                        this.testDestroyRow(cb);
                    })
                })
            }, 300)
        }
    },
    getRowNode(row){
        let arr = this.blockBoxNode.children.filter(item=>{
            return item.pos[0] == row
        });
        //console.log(arr);
        return arr;
    },
    //整体向上移动
    moveArena(cb){
        cc.tween(this.blockBoxNode).by(this.aniTime, {position: cc.v2(0, this.blockH)}).call(()=>{
            this.deletRow(0)
            cb&&cb()
        }).start();
    },
    //移除一行
    deletRow(rowNum){
        //console.log(this.arena[rowNum])
        if(this.arena[rowNum].every(item=>item == 0)){
            this.arena.splice(rowNum, 1);
            if(rowNum == 0){
                this.blockBoxNode.children.forEach(item=>{
                    item.pos = [--item.pos[0], item.pos[1]]
                    item.y =  -item.pos[0]*this.blockH
                })
            }
        }
        this.blockBoxNode.y-=this.blockH;
        setTimeout(()=>{
            this.moveRowDown(this.rowNum-2, (isDestroy)=>{
                if(!isDestroy){
                    //尝试下落之后 第0行还有元素，游戏结束
                    if(this.arena[0].some(item=>item != 0)){
                        this.callGameOver();
                    }
                }
            });
        })
    },
    //方块下落
    moveRowDown(row, cb){
        let subArena = this.arena.slice(row+1);
        let aTween = [];
        for(let i=row; i>=0; i--){
            let curRowData = this.arena[i];
            subArena = this.arena.slice(i+1);
            // if(i<row && aTween.length == 0){
            //     break;
            // }
            if(curRowData.some(item=>item != 0)){
                for(let col = 0; col<curRowData.length;){
                    let cur = curRowData[col];
                    if(cur != 0){
                        let curNode = this.getNodeByPos(i, col);
                        let downRow = 0;
                        for(let x = 0; x<subArena.length; x++){
                            let subRow = subArena[x].slice(col, col+cur);
                            if(subRow.every(item=>item==0)){
                                downRow = x+1;
                            }else{
                                break;
                            }
                        }
                        if(downRow != 0){
                            curNode.toPos = [i+downRow, col];
                            let tt = cc.tween(curNode).by(this.aniTime, 
                                {position: cc.v2(0, (curNode.pos[0]-curNode.toPos[0])*this.blockH)},{easing: 'bounceOut'}
                            );
    
                            for(let n=0; n<curNode.val; n++){
                                let c = this.arena[curNode.pos[0]][curNode.pos[1]+n];
                                this.arena[curNode.pos[0]][curNode.pos[1]+n] = this.arena[curNode.toPos[0]][curNode.toPos[1]+n];
                                this.arena[curNode.toPos[0]][curNode.toPos[1]+n] = c;
                            }
                            curNode.pos = curNode.toPos;
                            aTween.push(tt);
                        }
                        col+=cur;
                    }else{
                        col+=1;
                    }
                }
            }
        };
        if(aTween.length == 0){
            cb&&cb(false)
        }else{
            aTween.forEach((item, index)=>{
                item.call(()=>{
                    if(index == aTween.length-1){
                        console.log('animation finished')
                        cb&&cb(true)
                    }
                }).start()
            })
        }
    },
    //通过位置 获取节点
    getNodeByPos(row, col){
        let arr = this.blockBoxNode.children;
        arr = arr.filter(item=>{
            return item.pos[0] == row && item.pos[1] == col
        });
        if(arr.length == 0){
            console.log('no Node')
        }
        return arr[0];
    },
    //生成下一行数据
    createNextRowData(){
        let arr = [];
        let spaceBlock = this.getRnd(2, 5);
        arr = arr.concat(new Array(spaceBlock).fill(0));
        let num = this.colNum - spaceBlock;
        let n = 0;
        let arr2 = [];
        while(n != num){
            let cur = this.getRnd(1, 5);
            if(n+cur <= num){
                n += cur;
                arr2.push(cur);
            }
        }
        arr2.forEach(item=>{
            arr.push(new Array(item).fill(item))
        })
        arr.sort(()=>Math.random()-0.5);
        let res = [];
        arr.forEach(item=>{
            if(item.constructor == Array){
                res = res.concat(item)
            }else{
                res.push(item)
            }
        })
        //console.log('createNextRowData', res);
        //res = [2,2,0,0,0,0,0,2,2];
        this.previewRowData = res//.sort(()=>Math.random()-0.5);
        this.renderPreview();
    },
    //生成一行
    createRowData(){
        let row = this.previewRowData;
        this.arena.push(row)
        this.createNextRowData();
        return row
    },
    createBoomRow(row){
        let arr = this.boomRowNodeArr.filter(item=>{
            return item.isShow == false;
        })
        let boomRow = null;
        if(arr.length == 0){
            boomRow = cc.instantiate(this.boomRowPrefab);
            for(let i=0; i<this.colNum; i++){
                let block = cc.instantiate(this.boomBlockPrefab);
                block.setPosition(cc.v2(i*this.blockW, 0))
                block.width = this.blockW;
                block.height = this.blockH;
                boomRow.addChild(block);
            }
            this.arenaNode.addChild(boomRow)
            this.boomRowNodeArr.push(boomRow)            
        }else{
            boomRow = arr[0];
        }
        boomRow.setPosition(cc.v2(0, -row*this.blockH))
        boomRow.isShow = true;
        boomRow.active = true;
        boomRow.children.forEach(item=>{
            item.getComponent(cc.Animation).play('boom');
        })
        return boomRow;
    },
    setRowData(node, n){
        let row = this.arena[node.pos[0]];
        let col = node.pos[1];
        let val = node.val;

        if(n<0){
            for(let i=0; i<val; i++){
                let c = row[col+i];
                row[col+i] = row[col+i+n];
                row[col+i+n] = c;
            }
        }else{
            for(let i=val-1; i>=0; --i){
                let c = row[col+i];
                row[col+i] = row[col+i+n];
                row[col+i+n] = c;
            }
        }
    },
    //获取边界
    getBoundary(node){
        let pos = node.pos;
        let val = node.val;
        let row =  this.arena[pos[0]];
        let col = pos[1];
        let min = col-1;
        let max = col+(val-1)+1;
        while(true){
            if( (min < 0 || row[min] != 0) && (max == row.length || row[max] != 0) ){
                break;
            }
            if(min >= 0 && row[min] == 0){
                min--;
            }
            if(max < row.length && row[max] == 0){
                max++;
            }
        }
        min = min+1;
        max = max-(val-1)-1;
        return [min, max]
    },
    getRnd(m, n){
        return Math.floor(Math.random()*(n-m)+m)
    },    
    initArena(){
        for(let i=0; i<this.arena.length; i++){
            let row = this.arena[i];
            if(row.every(item=>item==0)){
                continue;
            }
            this.createRowNode(row, i)
        }
    },
    createRowNode(rowData, rowNum){
        //[0,1,0,1,0,1,0,1,0],
        for(let j=0; j<rowData.length;){
            let cur = rowData[j];
            if(cur != 0){
                this.createBlock(cur, rowNum, j);
                j+=cur;
            }else{
                j+=1;
            }
        }
    },
    renderPreview(){
        this.previewNode.removeAllChildren();
        for(let i=0; i<this.previewRowData.length;){
            let cur = this.previewRowData[i];
            if(cur != 0){
                this.createPreviewBlock(cur, i);
                i+=cur;
            }else{
                i+=1;
            }
        }
    },
    createBlock(n, row, col){
        let block = cc.instantiate(this.blockPrefab);
        block.setPosition(cc.v2(col*this.blockW, -row*this.blockH))
        block.width = this.blockW*n;
        block.height = this.blockH;
        let sprite = block.getComponent(cc.Sprite);
        let key = `${this.colorArr[this.getRnd(0, 4)]}${n}@2x`;
        sprite.spriteFrame = this.blockSpriteMap[key];
        block.pos = [row, col];
        block.val = n;
        this.initBlockEvent(block);
        this.blockBoxNode.addChild(block);
    },
    createPreviewBlock(n, col){
        let block = cc.instantiate(this.previewBlockPrefab);
        block.setPosition(cc.v2(col*this.blockW, 0))
        let sprite = block.getComponent(cc.Sprite);
        let key = `${n}@2x`;
        sprite.spriteFrame = this.previewBlockSpriteMap[key];
        this.previewNode.addChild(block);
    },
    onLoad () { 
        this._onLoad();
        //开启碰撞检测
        var manager = cc.director.getCollisionManager();
        manager.enabled = true;
        cc.resources.loadDir("blockImg", cc.SpriteFrame, (err, assets)=>{
            assets.forEach(item=>{
                this.blockSpriteMap[item._name] = item;
            })
            //this.initArena();
        });
        cc.resources.loadDir("nextRowImg", cc.SpriteFrame, (err, assets)=>{
            assets.forEach(item=>{
                this.previewBlockSpriteMap[item._name] = item;
            })
            //this.createNextRowData();
        });        
    },
    start () {
        
    },
    update (dt) {
        this._update(dt);
    },
});
